import java.net.*;
import java.io.*;
import java.util.*;

public class ChatroomServer {

   public static void main ( String args[] ) {
      ServerSocket	ssk = null;
      Socket		sk  = null;
      Broadcaster	bctr = new Broadcaster( ); 
      try {
	 ssk = new ServerSocket( 55667 );   // instantiate server socket object.
	 while ( true ) {
	    sk = ssk.accept ( );	    // server is listening for client's connection. If a client
					    // requested a connection, and the connect is made successfully
					    // then a socket object is returned.
	    new Thread( new Agent ( sk, bctr ) ).start();
         } 
       } catch ( IOException e ) { e.printStackTrace( ) ; }
     try { if ( ssk != null ) ssk.close( );  } catch ( IOException e ) { }

   }

}

// A monitor with shared data and syncchonized function.
class Broadcaster {

   public Vector< ObjectOutputStream>	outs = new Vector<ObjectOutputStream> ( 10 );
   Iterator<ObjectOutputStream>		itr = null;

   synchronized public void broadcast ( Object msg ) {
     try {
	 itr = outs.iterator() ;
	 System.out.printf( "%s will be sent to %d clients\n",
		 	msg, outs.size()) ; 
	while ( itr.hasNext() ) {
		itr.next().writeObject( msg );
	}
     } catch ( IOException e ) { e.printStackTrace() ;}
   }
}

class Agent implements Runnable {
   Socket		sk  = null;
   Broadcaster		bctr = null;
   ObjectOutputStream	out = null;
   ObjectInputStream	in = null;

   public Agent ( Socket sk, Broadcaster c ) {
      try {
	this.sk = sk; bctr = c;
	out = new ObjectOutputStream ( sk.getOutputStream ( )  );
	in  = new ObjectInputStream (  sk.getInputStream ( )  );
        bctr.outs.add ( out );
      } catch ( IOException e ) { e.printStackTrace ( ) ; }
   }

   public void run ( ) {  
     Object obj = null;
     try {	
	while ( true ) {
		obj = in.readObject() ;
		bctr.broadcast( obj );
        }
     } catch ( IOException e ) { e.printStackTrace(); bctr.outs.remove( out );  }
       catch ( ClassNotFoundException e2 ) { e2.printStackTrace(); bctr.outs.remove( out );  }

   }
}
